﻿-- Author      : Thorwald Odin
-- Create Date : 12/7/2013 1:51:54 PM
local _, addon = ...;

local L = addon.Locale;

local Filter = addon.Filter;

Filter.FilterActions = {};
Filter.ConfigFilterActions = {};

--[[
FilterActions.DataTable:
{
	Name = 'Filter Action unique ID string',
	Label = 'Localized name to use in the config screen',
	ActionFunc = function(FilterTable, ItemTable)
		-- take action on ItemTable
		-- return true if action was completed. false if the item should be returned to the filter stack
	end,
	Options = { -- AceConfig table. Root must be of type `group`.
		type = 'group',
	},
	Variables = { -- Default values for the variables used by this filter. Variable names should be unique among all possible variable names. Do not nest variables in levels of tables.
		ActionUID_Var1 = 'Value'1,
		ActionUID_Var2 = 2,
		-- ..., etc
		
		ActionUID_Vars = { -- This is very bad. Do not store variables like this.
			BadPractice = true,
			BadIdea = true,
			DoNotDoThis = true,
		},
	},
}
--]]

function addon:RegisterFilterAction(data)
	self:Assert(type(data) == 'table', 'Usage: LootFilter:RegisterFilterAction(table: FilterActions.DataTable)');
	
	local name, label = data.Name, data.Label;
	local action = data.ActionFunc;
	local opts, vars = data.Options, data.Variables;
	
	self:Assert(type(name) == 'string', 'Invalid type for .Name member. Expected `string`. Got `' .. type(name) .. '`.');
	self:Assert(type(label) == 'string', 'Invalid type for .Label member. Expected `string`. Got `' .. type(label) .. '`.');
	self:Assert(type(action) == 'function', 'Invalid type for .ActionFunc member. Expected `function`. Got `' .. type(action) .. '`.');
	self:Assert(type(opts) == 'table' or type(opts) == nil, 'Invalid type for .Options member. Expected `table` or `nil`. Got `' .. type(opts) .. '`.');
	self:Assert(type(vars) == 'table' or type(vars) == nil, 'Invalid type for .Variables member. Expected `table` or `nil`. Got `' .. type(vars) .. '`.');
	
	self:Assert(type(Filter.FilterActions[name]) == 'nil', 'Filter action `' .. name .. '` already registered.');
	
	if type(vars) ~= 'nil' then
		addon:AddDefaultVariables(vars);
	end
	
	data.Options = opts or false;
	
	Filter.FilterActions[name] = data;
end

function addon:GetFilterAction(filter)
	local t = Filter.FilterActions[filter.Action];
	return t.ActionFunc;
end

function addon:PerformFilterAction(filter, item)
	local func = self:GetFilterAction(filter);
	return func(filter, item);
end

function addon:UpdateConfigFilterActions()
	local t = Filter.ConfigFilterActions;
	for k, v in pairs(Filter.FilterActions) do
		t[k] = v.Label;
	end
end

function addon:GetFilterActionOptions()
	local t = {};
	for k, v in pairs(Filter.FilterActions) do
		if v.Options ~= false then
			t[k] = v.Options;
		end
	end
	return t;
end

function addon:GetFilterActionLabel(filter)
	return Filter.FilterActions[filter.Action].Label;
end

local function DeleteContainerItem(bag, slot, link, filter)
	if not addon.db.ConfigMode then
		PickupContainerItem(bag, slot);
		if CursorHasItem() then
			if addon.db.NotifyDelete then
				addon:PrintF(L['Delete {1}. ({2})'], link, addon:MakeFilterReason(filter));
			end
			DeleteCursorItem();
		end
	else
		addon:PrintF(L['Unable to delete {1} in config mode.'], link);
	end
end

--[[
FilterType.DataTable:
All built-in filter types (item type, item type&subtype, item quality, etc) are defined using this method and available near the end of this file.
{
	Name = 'Filter Type unique ID string',
	Label = 'Localized name to use in the config screen',
	ReasonFunc = function(FilterTable)
		return 'The localized description of why an item would match this filter';
	end,
	MatchFunc = function(FilterTable, ItemTable)
		if ItemTable['Whatever metric'] == FilterTable['Metric value'] then -- Return true if the item in ItemTable has the proper criteria/metrics/properties to match the filter in FilterTable.
			return true;
		else -- Otherwise false.
			return false;
		end
	end,
	Options = { -- AceConfig table. Root must be of type `group`.
		type = 'group',
	},
	Variables = { -- Default values for the variables used by this filter. Variable names should be unique among all possible variable names. Do not nest variables in levels of tables.
		FilterUID_Var1 = 'Value1',
		FilterUID_Var2 = 2,
		-- ..., etc
		
		FilterUID_Vars = { -- This is very bad. Do not store variables like this.
			BadPractice = true,
			BadIdea = true,
			DoNotDoThis = true,
		},
	},
}
--]]

Filter.FilterTypes = {}; -- [TypeName]=>DataTable
Filter.ConfigFilterTypes = {};

function addon:RegisterFilterType(data)
	self:Assert(type(data) == 'table', 'Usage: LootFilter:RegisterFilterType(table: FilterType.DataTable)');
	
	local name, label = data.Name, data.Label;
	local match, reason = data.MatchFunc, data.ReasonFunc;
	local opts, vars = data.Options, data.Variables;
	
	self:Assert(type(name) == 'string', 'Invalid type for .Name member. Expected `string`. Got `' .. type(name) .. '`.');
	self:Assert(type(label) == 'string', 'Invalid type for .Label member. Expected `string`. Got `' .. type(label) .. '`.');
	self:Assert(type(match) == 'function', 'Invalid type for .MatchFunc member. Expected `function`. Got `' .. type(match) .. '`.');
	self:Assert(type(reason) == 'function', 'Invalid type for .ReasonFunc member. Expected `function`. Got `' .. type(reason) .. '`.');
	self:Assert(type(opts) == 'table', 'Invalid type for .Options member. Expected `table`. Got `' .. type(opts) .. '`.');
	self:Assert(type(vars) == 'table' or type(vars) == nil, 'Invalid type for .Variables member. Expected `table` or `nil`. Got `' .. type(vars) .. '`.');
	
	self:Assert(type(Filter.FilterTypes[name]) == 'nil', 'Filter type `' .. name .. '` already registered.');
	
	if type(vars) ~= 'nil' then
		addon:AddFilterVariables(vars);
	end
	
	Filter.FilterTypes[name] = data;
end

function addon:GetFilterReasonFunction(filter)
	local t = Filter.FilterTypes[filter.Type];
	if t ~= nil then
		return t.ReasonFunc;
	end
	return nil;
end

function addon:MakeFilterReason(filter)
	local t = Filter.FilterTypes[filter.Type];
	if t then
		return addon:Format(t.ReasonFunc(filter));
	else
		return 'Unknown filter type: ' .. tostring(filter.Type);
	end
end

function addon:UpdateConfigFilterTypes()
	local t = Filter.ConfigFilterTypes;
	for k, v in pairs(Filter.FilterTypes) do
		t[k] = v.Label;
	end
end

function addon:GetFilterTypeOptions()
	local t = {};
	for k, v in next, Filter.FilterTypes do
		t[k] = addon:TableCopy_Deep(v.Options);
	end
	return t;
end

function addon:GetFilterMatchFunc(filter)
	local func = Filter.FilterTypes[filter.Type].MatchFunc;
	return func;
end

function addon:CheckFilterMatch(filter, item)
	local func = self:GetFilterMatchFunc(filter);
	return func(filter, item);
end

--[[
ValueMethods.DataTable:
{
	Name = 'Value method unique ID string',
	Label = 'Localized name to use in the config screen',
	ValueFunc = function(filter, item)
		return item.Value; -- Do some magic on the item value.
	end,
	ReasonTemplate = 'Localized reason template to use for this value method. single item, current stack size, maximum stack size, etc',
	Options = { -- Optional AceConfig table. Root must be of type `group`.
		type = 'group',
	},
}
--]]

Filter.ValueMethods = {};
Filter.ConfigValueMethods = {};

function addon:RegisterItemValueMethod(data)
	self:Assert(type(data) == 'table', 'Usage: LootFilter:RegisterItemValueMethod(table: ValueMethods.DataTable)');
	
	local name, label = data.Name, data.Label;
	local func, reason = data.ValueFunc, data.ReasonTemplate;
	local opts = data.Options;
	
	self:Assert(type(name) == 'string', 'Invalid type for .Name member. Expected `string`. Got `' .. type(name) .. '`.');
	self:Assert(type(label) == 'string', 'Invalid type for .Label member. Expected `string`. Got `' .. type(label) .. '`.');
	self:Assert(type(func) == 'function', 'Invalid type for .ValueFunc member. Expected `function`. Got `' .. type(func) .. '`.');
	self:Assert(type(reason) == 'string', 'Invalid type for .ReasonTemplate member. Expected `string`. Got `' .. type(reason) .. '`.');
	self:Assert(type(opts) == 'table' or type(opts) == 'nil', 'Invalid type for .Options member. Expected `table` or `nil`. Got `' .. type(opts) .. '`.');
	
	self:Assert(type(Filter.ValueMethods[name]) == 'nil', 'Item value method `' .. name .. '` alredy registered.');
	
	data.Options = opts or false;
	
	Filter.ValueMethods[name] = data;
end

function addon:LookupItemValueMethod(filter)
	local t = Filter.ValueMethods[filter.ItemValueMethod];
	if t then
		return t.ValueFunc, t.ReasonTemplate;
	else
		return nil, ''; -- Value function, reason template
	end
end

function addon:UpdateConfigValueMethods()
	local t = Filter.ConfigValueMethods;
	for k, v in pairs(Filter.ValueMethods) do
		t[k] = v.Label;
	end
end

function addon:GetValueMethodOptions()
	local t = {};
	for k, v in next, Filter.ValueMethods do
		if v.Options ~= false then
			t[k] = addon:TableCopy_Deep(v.Options);
		end
	end
	return t;
end

--[[
	Filter helper functions
--]]

local function no_op()
	return true;
end

function addon:IterateBagSpace(funcs)
	local onBag, onItem, onEmpty = no_op, no_op, no_op;
	if type(funcs) == 'table' then
		onBag = funcs.OnBag or no_op;
		onItem = funcs.OnItem or no_op;
		onEmpty = funcs.OnEmpty or no_op;
	end
	local link, slots;
	for bag = 0, 4 do
		if onBag(bag) then
			slots = GetContainerNumSlots(bag);
			for slot = 1, slots do
				link = GetContainerItemLink(bag, slot);
				if link then
					if onItem(bag, slot, link) == false then
						return;
					end
				else
					if onEmpty(bag, slot) == false then
						return;
					end
				end
			end
		end
	end
end

function addon:DeleteItem(item, filter)
	if not item or not item.Bag or item.Bag < 0 then
		addon:Debug('warning', 'Invalid .bag or .Slot members. .Bag:{1}, .Slot:{2}', item.Bag, item.Slot);
		return;
	end
	if addon.db.Confirm then
		Filter.ConfirmDelete(item, filter);
	else
		DeleteContainerItem(item.Bag, item.Slot, item.Link, filter);
	end
end

Filter.ConfirmDelete = function(item, filter)
	addon.DialogData = {
		Item = item,
		Filter = filter;
	};
	if not StaticPopupDialogs['LootFilterConfirmDelete'] then
		StaticPopupDialogs['LootFilterConfirmDelete'] = {
			text = DELETE_ITEM,
			button1 = YES,
			button2 = NO,
			OnShow = function(self)
				addon.InDialog = true;
			end,
			OnHide = function()
				addon.DialogData.Item:Release();
				addon.InDialog = false;
				addon:LOOT_CLOSED();
			end,
			OnAccept = function()
				local item = addon.DialogData.Item;
				local link = GetContainerItemLink(item.Bag, item.Slot);
				if not link then
					addon:Debug('warning', 'No item at position: {1}.{2}', item.Bag, item.Slot);
					return;
				end
				local name = GetItemInfo(link);
				if not name or name ~= item.Name then
					addon:Debug('warning', 'Incorrect item at position: {1},{2}. Found: {3}. Expected: {4}.', item.Bag, item.Slot, name, item.Name);
					return;
				end
				-- print delete message
				DeleteContainerItem(item.Bag, item.Slot, item.Link, addon.DialogData.Filter);
			end,
			OnCancel = function()
				-- @TODO add prompt for why to not delete the item
			end,
			timeout = 0,
			exclusive = 1,
			showAlert = 1,
			hideOnEscape = 1
		};
	end
	StaticPopup_Show("LootFilterConfirmDelete", item.Link);
end

--[[
	Internal-use sorting methods
--]]

Filter.SortFunctions = {
	ByFilter = function(a, b)
		if a.Weight == b.Weight then
			return a.Action < b.Action; -- Keep over Delete
		end
		return a.Weight > b.Weight;
	end,
	ByItem = function(a, b)
		return Filter.SortFunctions(a.Filter, b.Filter);
	end,
};

--[[
	Filter methods
--]]

Filter.Methods = {};

Filter.Methods.Delayed = function()
	if InCombatLockdown() then
		addon:Schedule(addon.db.FilterDelay, {Function = addon.Filter.Methods.Delayed});
		return;
	end
	
	if not addon.db.Enabled or addon.InLoot or addon.InDialog then
		addon:Debug('notice', 'Enabled: {1}, InLoot: {2}, Combat: {3}, InDialog: {4}', addon.db.Enabled, addon.InLoot, InCombatLockdown(), addon.InDialog);
		return;
	end
	
	local slotsFree = 0;
	addon:IterateBagSpace({
		OnBag = function(bag)
			return addon.db.ParseBag[bag + 4];
		end,
		OnEmpty = function(bag, slot)
			slotsFree = slotsFree + 1;
		end,
	});
	
	if slotsFree >= addon.db.Delayed.Threshold then -- no need to delete anymore items
		addon:Debug('notice', 'slotsFree >= Threshold ({1} >= {2})', slotsFree, addon.db.Delayed.Threshold);
		while addon.ItemStack[1] do -- make sure the item stack is clean
			(addon.ItemStack[1]):Release();
			table.remove(addon.ItemStack, 1);
		end
		return;
	end
	
	if #addon.ItemStack > 0 then
		local item = addon.ItemStack[1];
		
		if addon:PerformFilterAction(item.Filter, item) then
			table.remove(addon.ItemStack, 1);
			if not addon.InDialog then
				item:Release();
			end
		end
	else -- build list of items
		local stack = addon:AcquireTable();
		local slots, item, link = 0, nil, nil;
		for bag = 0, 4 do
			if addon.db.ParseBag[bag + 1] then
				slots = GetContainerNumSlots(bag);
				for slot = 1, slots do
					link = GetContainerItemLink(bag, slot);
					if link then
						item = addon.Item.New(link, bag, slot);
						if item then
							table.insert(stack, item);
						end
					end
				end
			end
		end
		
		table.sort(addon.db.Filters, addon.Filter.SortFunctions.ByItem);
		
		local action = false;
		while stack[1] do
			item = stack[1];
			action = false;
			
			for i, v in ipairs(addon.db.Filters) do
				if v.Enabled and addon:CheckFilterMatch(v, item) then
					item.Filter = v;
					action = v.Action ~= 'Keep';
					break;
				end
			end
			
			if action then
				table.insert(addon.ItemStack, item);
			else
				item:Release();
			end
			
			table.remove(stack, 1);
		end
		
		table.sort(addon.ItemStack, addon.Filter.SortFunctions.ByItem);
	end
	
	if #addon.ItemStack > 0 then
		addon:Schedule(addon.db.FilterDelay, {Function = addon.Filter.Methods.Delayed});
	end
end

Filter.Methods.Increment = function()
	if InCombatLockdown() then
		addon:Schedule(addon.db.FilterDelay, {Function = addon.Filter.Methods.Increment});
		return;
	end

	if not addon.db.Enabled or addon.InLoot or addon.InDialog then
		addon:Debug('notice', 'Enabled: {1}, InLoot: {2}, Combat: {3}, InDialog: {4}', addon.db.Enabled, addon.InLoot, InCombatLockdown(), addon.InDialog);
		return;
	end
	
	if #addon.LootStack > 0 then
		local link, item;
		repeat
			link = addon.LootStack[1];
			if link then
				item = addon.Item.New(link);
				if item then
					table.insert(addon.ItemStack, item);
				end
			end
			table.remove(addon.LootStack, 1);
		until not link;
	elseif #addon.ItemStack > 0 then
		local item = addon.ItemStack[1];
		
		table.sort(addon.db.Filters, addon.Filter.SortFunctions.ByFilter);
		
		local match = false;
		local ret = false;
		for i, v in ipairs(addon.db.Filters) do
			if v.Enabled and addon:CheckFilterMatch(v, item) then -- matches the filter
				match = true;
				ret = addon:PerformFilterAction(v, item)
				break; -- we have an action, so pop out of the loop to avoid matching to a lower priority filter
			end
		end
		
		if not match and addon.db.NotifyIgnore then
			addon:PrintF(L['No action taken on {1}.'], item.Link);
		end
		
		if ret or not match then -- only remove the item if the action "succeeded" or if the item does not match any filters
			table.remove(addon.ItemStack, 1);
		
			if not addon.InDialog then
				item:Release();
			end
		end
	end
	
	if #addon.ItemStack > 0 then
		addon:Schedule(addon.db.FilterDelay, {Function = addon.Filter.Methods.Increment});
	end
end

Filter.Methods.Demand = function()
	addon:Debug('notice', 'Filter.Methods.Demand');
end

--[[
	Built-in filter action
--]]

addon:RegisterFilterAction({
	Name = 'Keep',
	Label = L['Keep an item'],
	ActionFunc = function(filter, item)
		if addon.db.NotifyKeep then
			addon:PrintF(L['Kept {1}. ({2})'], item.Link, addon:MakeFilterReason(filter));
		end
		return true;
	end,
	Options = {
		type = 'group',
		name = L['Keep options'],
		args = {
			NotifyKeep = {
				type = 'toggle',
				name = L['Notify on keep action'],
				desc = L['Print a message to the chat frame when an item is kept.'],
			},
		},
	},
	Variables = {
		NotifyKeep = true,
	},
});

addon:RegisterFilterAction({
	Name = 'Delete',
	Label = L['Delete an item'],
	ActionFunc = function(filter, item)
		addon:DeleteItem(item, filter);
		return true;
	end,
	Options = {
		type = 'group',
		name = L['Delete options'],
		args = {
			NotifyDelete = {
				type = 'toggle',
				order = 1,
				name = L['Notify on delete'],
				desc = L['Print a message to the chat frame when an item is deleted.'],
			},
			Confirm = {
				type = 'toggle',
				order = 2,
				name = L['Confirm item deletion'],
				desc = L['Show a confirmation dialog when deleting a file'],
			},
		},
	},
	Variables = {
		Confirm = true,
		NotifyDelete = true,
	},
});

--[[
	Build-in item value methods
--]]

addon:RegisterItemValueMethod({
	Name = 'ItemValueSingle',
	Label = L['Single item'],
	ValueFunc = function(filter, item)
		return item.Value;
	end,
	ReasonTemplate = L['Value of a single item'],
});

addon:RegisterItemValueMethod({
	Name = 'ItemValueCurrentStack',
	Label = L['Current stack value'],
	ValueFunc = function(filter, item)
		return item.Value * item.Count;
	end,
	ReasonTemplate = L['Value of the current stack'],
});

addon:RegisterItemValueMethod({
	Name = 'ItemValueMaximumStack',
	Label = L['Maximum stack value'],
	ValueFunc = function(filter, item)
		return item.Value * item.MaxStackSize;
	end,
	ReasonTemplate = L['Value of a full stack'],
});

--[[
	Built-in filter types
--]]

addon:RegisterFilterType({
	Name = 'ItemType',
	Label = L['Item Type'],
	ReasonFunc = function(filter)
		return L['Item type is {1}'], addon.ItemTypes[filter.ItemType];
	end,
	MatchFunc = function(filter, item)
		return filter.ItemType == item.Type;
	end,
	Options = {
		type = 'group',
		name = L['Item type filter options'],
		args = {
			ItemType = {
				type = 'select',
				order = 1,
				name = L['Item Type'],
				desc = L['Type of items to match (e.g. Weapon, Armor, Trade Goods, etc)'],
				values = function()
					return addon.ItemTypes;
				end,
			},
		},
		hidden = function(info)
			return info.arg.db.Type ~= 'ItemType';
		end,
	},
	Variables = {
		ItemType = 1,
	},
});

addon:RegisterFilterType({
	Name = 'ItemSubType',
	Label = L['Item Type and Sub-Type'],
	ReasonFunc = function(filter)
		return L['Item Type is {1} and Sub-Type is {2}'], addon.ItemTypes[filter.ItemType], addon.ItemSubTypes[filter.ItemType][filter.ItemSubType];
	end,
	MatchFunc = function(filter, item)
		return filter.ItemType == item.Type and filter.ItemSubType == item.SubType;
	end,
	Options = {
		type = 'group',
		name = L['Item type and sub-type filter options'],
		args = {
			ItemType = {
				type = 'select',
				order = 1,
				name = L['Item Type'],
				desc = L['Type of items to match (e.g. Weapon, Armor, Trade Goods, etc)'],
				values = function()
					return addon.ItemTypes;
				end,
			},
			ItemSubType = {
				type = 'select',
				order = 2,
				name = L['Item Sub-Type'],
				desc = L['Sub type of items to match (e.g. Swords, Shields, Gems, etc)'],
				values = function(info)
					return addon.ItemSubTypes[info.arg.db.ItemType];
				end,
			},
		},
		hidden = function(info)
			return info.arg.db.Type ~= 'ItemSubType';
		end,
	},
	Variables = {
		ItemType = 1,
		ItemSubType = 1,
	},
});

addon:RegisterFilterType({
	Name = 'ItemQuality',
	Label = L['Item Quality'],
	ReasonFunc = function(filter)
		local t = addon.ItemQuality;
		if ENABLE_COLORBLIND_MODE == '1' then
			t = addon.ItemQualityColorBlind;
		end
		return L['Item Quality is {1}'], t[filter.ItemQuality];
	end,
	MatchFunc = function(filter, item)
		return filter.ItemQuality == item.Quality;
	end,
	Options = {
		type = 'group',
		name = L['Item quality filter options'],
		args = {
			ItemQuality = {
				type = 'select',
				order = 1,
				name = L['Item Quality'],
				desc = L['Quality or rarity of items to match (e.g. Green, Blue, Purple, Rare, Epic, etc)'],
				values = function()
					if ENABLE_COLORBLIND_MODE == '1' then
						return addon.ItemQualityColorBlind;
					else
						return addon.ItemQuality;
					end
				end,
			},
		},
		hidden = function(info)
			return info.arg.db.Type ~= 'ItemQuality';
		end,
	},
	Variables = {
		ItemQuality = 0,
	},
});

addon:RegisterFilterType({
	Name = 'ItemValue',
	Label = L['Item Value'],
	ReasonFunc = function(filter)
		local _, template = addon:LookupItemValueMethod(filter);
		local str = L['{1} is at least {2}']; -- Keep action
		
		if filter.Action == 'Delete' then -- Delete action
			str = L['{1} is at most {2}'];
		end
		
		local gold, silver, copper, moneyString = 0, 0, 0, '';
		
		gold = floor(filter.ItemValue / COPPER_PER_GOLD);
		silver = floor((filter.ItemValue - (gold * COPPER_PER_GOLD)) / COPPER_PER_SILVER);
		copper = mod(filter.ItemValue, COPPER_PER_SILVER);
		
		if gold > 0 then
			if ENABLE_COLORBLIND_MODE == '1' then
				moneyString = moneyString .. string.format('%d' .. GOLD_AMOUNT_SYMBOL, gold);
			else
				moneyString = moneyString .. string.format(GOLD_AMOUNT_TEXTURE, gold, 0, 0);
			end
		end
		
		if silver > 0 then
			if ENABLE_COLORBLIND_MODE == '1' then
				moneyString = moneyString .. string.format('%d' .. SILVER_AMOUNT_SYMBOL, silver);
			else
				moneyString = moneyString .. string.format(SILVER_AMOUNT_TEXTURE, silver, 0, 0);
			end
		end
		
		if copper > 0 or moneyString == '' then
			if ENABLE_COLORBLIND_MODE == '1' then
				moneyString = moneyString .. string.format('%d' .. COPPER_AMOUNT_SYMBOL, copper);
			else
				moneyString = moneyString .. string.format(COPPER_AMOUNT_TEXTURE, copper, 0, 0);
			end
		end
		
		return str, template, moneyString;
	end,
	MatchFunc = function(filter, item)
		local func = addon:LookupItemValueMethod(filter);
		local value = func(filter, item) or 0;
		if filter.Action == 'Delete' then
			return filter.ItemValue > value;
		else
			return filter.ItemValue <= value;
		end
	end,
	Options = {
		type = 'group',
		name = L['Item value filter options'],
		args = {
			ItemValue = {
				type = 'input',
				dialogControl = 'AceGUIWidget-MoneyInput',
				order = 1,
				name = L['Item value'],
				desc = L['Value of the item using the specified value method.'],
				get = function(info)
					return tostring(info.arg.db.ItemValue);
				end,
				set = function(info, value)
					info.arg.db.ItemValue = tonumber(value) or 0;
					Filter.ConfigFunctions.UpdateOrderDesc(info);
				end,
			},
			ItemValueMethod = {
				type = 'select',
				order = 2,
				name = L['Item value method'],
				desc = L['Method used to calculate the value of an item'],
				values = function(info)
					addon:UpdateConfigValueMethods();
					return Filter.ConfigValueMethods;
				end,
			},
		},
		hidden = function(info)
			return info.arg.db.Type ~= 'ItemValue';
		end,
	},
	Variables = {
		ItemValue = 200, -- 2 silver
		ItemValueMethod = 'ItemValueSingle', -- single item
	},
});

addon:RegisterFilterType({
	Name = 'ItemNameContains',
	Label = L['Item name contains'],
	ReasonFunc = function(filter)
		return L['Item Name contains `{1}`.'], filter.ItemName;
	end,
	MatchFunc = function(filter, item)
		return item.Name:find(filter.ItemName) ~= nil;
	end,
	Options = {
		type = 'group',
		name = L['Item name contains filter options'],
		args = {
			ItemName = {
				type = 'input',
				order = 1,
				name = L['Item name contains:'],
				desc = L['Text to look for in the name of an item. This can be a Lua pattern.'],
			},
		},
		hidden = function(info)
			return info.arg.db.Type ~= 'ItemNameContains';
		end,
	},
	Variables = {
		ItemName = '',
	},
});

addon:RegisterFilterType({
	Name = 'ItemDescContains',
	Label = L['Item description contains'],
	ReasonFunc = function(filter)
		return L['Item Description contains `{1}`.'], filter.ItemDesc;
	end,
	MatchFunc = function(filter, item)
		return item.Desc:find(filter.ItemDesc) ~= nil;
	end,
	Options = {
		type = 'group',
		name = L['Item name contains filter options'],
		args = {
			ItemDesc = {
				type = 'input',
				order = 1,
				name = L['Item description contains:'],
				desc = L['Text to look for in the description of an item. This can be a Lua pattern.'],
			},
		},
		hidden = function(info)
			return info.arg.db.Type ~= 'ItemDescContains';
		end,
	},
	Variables = {
		ItemDesc = '',
	},
});


